package frame.laxcen.util;

import frame.laxcen.BaseFrame;
import frame.laxcen.collection.ConstPair;
import frame.laxcen.exception.IllegalFrameFormatException;
import frame.laxcen.response.BaseResponse;
import frame.util.ByteUtil;
import frame.util.FlowUtil;
import frame.util.Ref;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Predicate;

// 用于处理帧数据的工具类
public class FrameUtil {

    // 打印帧数据
    public static void printFrame(BaseFrame frame) {

        printFrame(frame.FrameBytes);
    }

    // 打印帧数据
    public static void printFrame(byte[] frame) {

        StringBuilder sb = new StringBuilder();

        for(int i = 0; i < frame.length; ++i) sb = sb.append(Integer.toHexString(frame[i] & 0xff)).append(' ');

        System.out.println(sb.toString());
    }

    // 打印解析后获得Response的内容
    public static <T extends BaseResponse> void printResponse(T response) {

        FlowUtil.silent(() -> response.getContentMap().forEach((k, v) -> System.out.println(k + " : " + v)));
    }

    // 根据分类函数将某数据帧分类到不同的结果中
    public static void dispatch(BaseFrame frame, List<BaseFrame> trueResult, List<BaseFrame> falseResult, Predicate<BaseFrame> splitCondition) {

        if(splitCondition.test(frame)) trueResult.add(frame);
        else falseResult.add(frame);
    }

    // 根据分类函数将列表中的每一帧分类到不同的结果中
    public static void split(List<BaseFrame> frames, List<BaseFrame> trueResult, List<BaseFrame> falseResult, Predicate<BaseFrame> splitCondition) {

        for(BaseFrame frame : frames)
            if(splitCondition.test(frame)) trueResult.add(frame);
            else falseResult.add(frame);
    }

    // 尝试从byte数组中解析数据帧 允许byte数组中容纳多个帧 会跳过错误的帧 忽略不完整的帧 忽略CRC校验错误的帧
    public static ConstPair<List<BaseFrame>, Integer> disjoint(byte[] block) {

        int offset = block.length - 1;

        List<BaseFrame> baseFrames = new LinkedList<>();

        for(int i = 0; i < block.length; ++i) {

            // i     -> 0xaa
            // i + 1 -> CW.H
            // i + 2 -> CW.L
            // i + 3 -> DataLength.H
            // i + 4 -> DataLength.L

            if(block[i] != (byte) 0xaa) continue;

            // HEAD.length = 1
            // CW.length = 2
            // DataLength.length = 2
            // Data.length = DataLength.value
            // CRC.length = 2

            int dataLength_H = i + 3, dataLength_L = i + 4;

            if(dataLength_L >= block.length) continue;

            int dataLength = ByteUtil.bytePairToInt(block[dataLength_H], block[dataLength_L]);

            int frameBegin = i, frameEndClosed = frameBegin + 4 + dataLength + 2;

            if(frameEndClosed >= block.length) continue;

            Ref<BaseFrame> ref = Ref.empty();

            try {

                ref.swap(new BaseFrame(Arrays.copyOfRange(block, frameBegin, frameEndClosed + 1)));

            } catch(IllegalFrameFormatException e) {

                e.printStackTrace();
            }

            if(ref.isPresent() && baseFrames.add(ref.get())) i = frameEndClosed;

            if(ref.isPresent()) offset = frameEndClosed + 1;
        }

        return ConstPair.of(baseFrames, offset);
    }

    public static void recycle(ByteBuffer buffer, byte[] block, int offset) {

        if(offset < block.length) buffer.put(Arrays.copyOfRange(block, offset, block.length));
    }
}